package com.duofan.gmall.order.service.impl;

import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.duofan.gmall.common.exception.NoStockException;
import com.duofan.gmall.common.to.SkuHasStockTo;
import com.duofan.gmall.common.to.mq.OrderTo;
import com.duofan.gmall.common.to.mq.SeckillOrderTo;
import com.duofan.gmall.common.utils.R;
import com.duofan.gmall.common.vo.MemberRespVo;
import com.duofan.gmall.order.config.MyRabbitMQConfig;
import com.duofan.gmall.order.constant.OrderConstant;
import com.duofan.gmall.order.entity.OrderItemEntity;
import com.duofan.gmall.order.entity.PaymentInfoEntity;
import com.duofan.gmall.order.enume.OrderStatusEnum;
import com.duofan.gmall.order.feign.CartFeignService;
import com.duofan.gmall.order.feign.MemberFeignService;
import com.duofan.gmall.order.feign.ProductFeignService;
import com.duofan.gmall.order.feign.WareFeignService;
import com.duofan.gmall.order.interceptor.LoginUserInterceptor;
import com.duofan.gmall.order.service.OrderItemService;
import com.duofan.gmall.order.service.PaymentInfoService;
import com.duofan.gmall.order.to.OrderCreateTo;
import com.duofan.gmall.order.vo.*;
//import io.seata.spring.annotation.GlobalTransactional;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.duofan.gmall.common.utils.PageUtils;
import com.duofan.gmall.common.utils.Query;

import com.duofan.gmall.order.dao.OrderDao;
import com.duofan.gmall.order.entity.OrderEntity;
import com.duofan.gmall.order.service.OrderService;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;


@Service("orderService")
public class OrderServiceImpl extends ServiceImpl<OrderDao, OrderEntity> implements OrderService {

    @Autowired
    MemberFeignService memberFeignService;
    @Autowired
    CartFeignService cartFeignService;
    @Autowired
    ThreadPoolExecutor executor;
    @Autowired
    WareFeignService wareFeignService;
    @Autowired
    StringRedisTemplate redisTemplate;
    @Autowired
    ProductFeignService productFeignService;
    @Autowired
    OrderItemService orderItemService;
    @Autowired
    RabbitTemplate rabbitTemplate;
    @Autowired
    PaymentInfoService paymentInfoService;

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<OrderEntity> page = this.page(
                new Query<OrderEntity>().getPage(params),
                new QueryWrapper<OrderEntity>()
        );

        return new PageUtils(page);
    }

    @Override
    public OrderConfirmVo confirmOrder() throws ExecutionException, InterruptedException {
        OrderConfirmVo confirmVo = new OrderConfirmVo();
        MemberRespVo loginUser = LoginUserInterceptor.loginUser.get();
        // 解决异步请求头丢失问题
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        CompletableFuture<Void> addressFuture = CompletableFuture.runAsync(() -> {
            RequestContextHolder.setRequestAttributes(requestAttributes);
            // 1.远程查询地址列表
            List<MemberAddressVo> addresses = memberFeignService.getAddresses(loginUser.getId());
            confirmVo.setAddress(addresses);
        }, executor);

        CompletableFuture<Void> itemsFuture = CompletableFuture.runAsync(() -> {
            RequestContextHolder.setRequestAttributes(requestAttributes);
            // 2.远程查询所有选中的购物项
            List<OrderItemVo> items = cartFeignService.getCurrentUserCartItems();
            confirmVo.setItems(items);
        }, executor).thenRunAsync(() -> {
            // 查询库存信息
            List<Long> skuIds = confirmVo.getItems().stream().map(OrderItemVo::getSkuId).collect(Collectors.toList());
            R r = wareFeignService.getSkusHasStock(skuIds);
            List<SkuHasStockTo> data = r.getData(new TypeReference<List<SkuHasStockTo>>() {});
            if(data != null) {
                Map<Long, Boolean> stocks = data.stream().collect(Collectors.toMap(SkuHasStockTo::getSkuId, SkuHasStockTo::getHasStock));
                confirmVo.setStocks(stocks);
            }
        }, executor);

        CompletableFuture.allOf(addressFuture, itemsFuture).get();

        // 3.查询用户积分信息
        Integer integration = loginUser.getIntegration();
        confirmVo.setIntegration(integration);

        String token = UUID.randomUUID().toString().replace("-", "");
        confirmVo.setOrderToken(token);
        redisTemplate.opsForValue().set(OrderConstant.USER_ORDER_TOKEN_PRE + loginUser.getId().toString(), token, 30, TimeUnit.MINUTES);

        return confirmVo;
    }

    // 事务传播机制演示
//    @Transactional
//    public void a() {
//        OrderServiceImpl orderService = (OrderServiceImpl) AopContext.currentProxy();
//        // 与a共用事务
//        orderService.b();
//        // 新事务
//        orderService.c();
//        // b回滚,c不回滚
//        int i = 1 / 0;
//    }
//
//    @Transactional(propagation = Propagation.REQUIRED)
//    public void b() {}
//
//    @Transactional(propagation = Propagation.REQUIRES_NEW)
//    public void c() {}

//    @GlobalTransactional
    @Transactional
    @Override
    public OrderSubmitRespVo submitOrder(OrderSubmitVo orderSubmitVo) throws NoStockException {
        OrderSubmitRespVo respVo = new OrderSubmitRespVo();
        respVo.setCode(0);
        MemberRespVo loginUser = LoginUserInterceptor.loginUser.get();
        String orderToken = orderSubmitVo.getOrderToken();
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        // 原子验令牌并删除
        // 脚本返回0或1,值0表示令牌失败,1表示成功
        Long res = redisTemplate.execute(new DefaultRedisScript<Long>(script, Long.class),
                Arrays.asList(OrderConstant.USER_ORDER_TOKEN_PRE + loginUser.getId()), orderToken);
        if(res == 0L) {
            // 令牌验证失败
            respVo.setCode(1);
            return respVo;
        }
        OrderCreateTo order = createOrder(orderSubmitVo);
        BigDecimal payAmount = order.getOrder().getPayAmount();
        BigDecimal payPrice = orderSubmitVo.getPayPrice();
        if(Math.abs(payAmount.subtract(payPrice).doubleValue()) < 0.01) {
            // 验价成功
            // 保存订单
            saveOrder(order);
            // 锁定库存:只要有异常,回滚订单数据
            lockStock(order);
//            int i = 1 / 0;
            try {
                // TODO 创建MQ消息表
                rabbitTemplate.convertAndSend(MyRabbitMQConfig.ORDER_EVENT_EXCHANGE,
                        MyRabbitMQConfig.ORDER_CREATE_ORDER_ROUTING_KEY, order.getOrder());
            } catch (Exception e) {
                // TODO 重试发送
            }

        } else {
            // 验价失败
            respVo.setCode(3);
        }
        respVo.setOrder(order.getOrder());
        return respVo;
    }

    @Override
    public OrderEntity getOrderByOrderSn(String orderSn) {
        OrderEntity orderEntity = this.getOne(new QueryWrapper<OrderEntity>().eq("order_sn", orderSn));
        return orderEntity;
    }

    @Override
    public void closeOrder(OrderEntity orderEntity) {
        // 关闭订单之前先查订单当前状态
        OrderEntity order = this.getById(orderEntity.getId());
        if(order.getStatus() == OrderStatusEnum.CREATE_NEW.getCode()) {
            OrderEntity update = new OrderEntity();
            update.setId(order.getId());
            update.setStatus(OrderStatusEnum.CANCLED.getCode());
            this.updateById(update);
            OrderTo orderTo = new OrderTo();
            BeanUtils.copyProperties(order, orderTo);
            rabbitTemplate.convertAndSend(MyRabbitMQConfig.ORDER_EVENT_EXCHANGE, "order.release.other", orderTo);
        }
    }

    @Override
    public PayVo getOrderPay(String orderSn) {
        PayVo payVo = new PayVo();
        OrderEntity orderEntity = this.getOrderByOrderSn(orderSn);
        payVo.setOut_trade_no(orderSn);
        // 2位有效数字,如有小数,向上取值
        BigDecimal bigDecimal = orderEntity.getPayAmount().setScale(2, BigDecimal.ROUND_UP);
        payVo.setTotal_amount(bigDecimal.toString());

        List<OrderItemEntity> itemEntities = orderItemService.list(new QueryWrapper<OrderItemEntity>().eq("order_sn", orderSn));
        OrderItemEntity itemEntity = itemEntities.get(0);
        // 默认标题设置为第一项sku名称
        payVo.setSubject(itemEntity.getSkuName());
        // 默认备注设置为销售属性
        payVo.setBody(itemEntity.getSkuAttrsVals());
        return payVo;
    }

    @Override
    public PageUtils queryPageWithItem(Map<String, Object> params) {
        MemberRespVo loginUser = LoginUserInterceptor.loginUser.get();
        IPage<OrderEntity> page = this.page(
                new Query<OrderEntity>().getPage(params),
                // 根据会员id查询,按照订单id降序
                new QueryWrapper<OrderEntity>().eq("member_id", loginUser.getId()).orderByDesc("id")
        );

        List<OrderEntity> orderEntities = page.getRecords().stream().map(order -> {
            List<OrderItemEntity> itemEntities = orderItemService.list(new QueryWrapper<OrderItemEntity>().eq("order_sn", order.getOrderSn()));
            order.setItemEntities(itemEntities);
            return order;
        }).collect(Collectors.toList());

        page.setRecords(orderEntities);
        return new PageUtils(page);
    }

    @Override
    public String handlePayResult(PayAsyncVo vo) {
        // 1.记录支付流水,方便后续对账
        String orderSn = vo.getOut_trade_no();

        PaymentInfoEntity entity = new PaymentInfoEntity();
        // 支付宝流水号
        entity.setAlipayTradeNo(vo.getTrade_no());
        // 订单号
        entity.setOrderSn(orderSn);
        // 支付状态
        entity.setPaymentStatus(vo.getTrade_status());
        // 通知时间
        entity.setCallbackTime(vo.getNotify_time());
        paymentInfoService.save(entity);


        // 2.修改订单状态
        if(vo.getTrade_status().equals("TRADE_SUCCESS") || vo.getTrade_status().equals("TRADE_FINISHED")) {
            baseMapper.updateOrderStatus(orderSn, OrderStatusEnum.PAYED.getCode());
        }

        return "success";
    }

    @Override
    public void createSeckillOrder(SeckillOrderTo orderTo) {
        // TODO 保存订单信息
        OrderEntity orderEntity = new OrderEntity();
        orderEntity.setOrderSn(orderTo.getOrderSn());
        orderEntity.setMemberId(orderTo.getMemberId());
        orderEntity.setStatus(OrderStatusEnum.CREATE_NEW.getCode());
        BigDecimal payAmount = orderTo.getSeckillPrice().multiply(new BigDecimal(orderTo.getNum().toString()));
        // TODO 运费处理
        orderEntity.setPayAmount(payAmount);
        this.save(orderEntity);
        // TODO 保存订单项信息
        OrderItemEntity itemEntity = new OrderItemEntity();
        itemEntity.setOrderSn(orderTo.getOrderSn());
        itemEntity.setRealAmount(payAmount);
        itemEntity.setSkuQuantity(orderTo.getNum());

        // TODO 获取当前SKU的SPU详细信息
        orderItemService.save(itemEntity);
    }

    /**
     * 锁库存
     * @param order
     */
    private void lockStock(OrderCreateTo order) throws NoStockException {
        WareSkuLockVo lockVo = new WareSkuLockVo();
        lockVo.setOrderSn(order.getOrder().getOrderSn());
        List<OrderItemVo> locks = order.getOrderItems().stream().map(item -> {
            OrderItemVo vo = new OrderItemVo();
            vo.setSkuId(item.getSkuId());
            vo.setCount(item.getSkuQuantity());
            vo.setTitle(item.getSkuName());
            return vo;
        }).collect(Collectors.toList());
        lockVo.setLocks(locks);
        R r = wareFeignService.orderLockStock(lockVo);
        if(r.getCode() != 0) {
            throw new NoStockException((String) r.get("msg"));
        }
    }

    /**
     * 保存订单数据
     * @param order
     */
    private void saveOrder(OrderCreateTo order) {
        OrderEntity orderEntity = order.getOrder();
        orderEntity.setModifyTime(new Date());
        this.save(orderEntity);
        List<OrderItemEntity> orderItemEntities = order.getOrderItems();
        orderItemService.saveBatch(orderItemEntities);
    }

    /**
     * 构建订单TO
     * @param orderSubmitVo
     * @return
     */
    private OrderCreateTo createOrder(OrderSubmitVo orderSubmitVo) {
        OrderCreateTo createTo = new OrderCreateTo();
        // 生成订单号
        String orderSn = IdWorker.getTimeId();
        OrderEntity orderEntity = createOrderEntity(orderSubmitVo, orderSn);
        createTo.setOrder(orderEntity);
        List<OrderItemEntity> orderItemEntities = createOrderItems(orderSn);
        createTo.setOrderItems(orderItemEntities);
        computePrice(orderEntity, orderItemEntities);
        return createTo;
    }

    /**
     * 价格计算
     * @param orderEntity
     * @param itemEntities
     */
    private void computePrice(OrderEntity orderEntity, List<OrderItemEntity> itemEntities) {
        // 订单总额
        BigDecimal totalAmount = new BigDecimal("0.0");
        BigDecimal couponAmount = new BigDecimal("0.0");
        BigDecimal integrationAmount = new BigDecimal("0.0");
        BigDecimal promotionAmount = new BigDecimal("0.0");
        int growth = 0;
        int integration = 0;
        for (OrderItemEntity itemEntity : itemEntities) {
            totalAmount = totalAmount.add(itemEntity.getRealAmount());
            couponAmount = couponAmount.add(itemEntity.getCouponAmount());
            integrationAmount = integrationAmount.add(itemEntity.getIntegrationAmount());
            promotionAmount = promotionAmount.add(itemEntity.getPromotionAmount());
            growth = growth + itemEntity.getGiftGrowth();
            integration = integration + itemEntity.getGiftIntegration();
        }
        orderEntity.setTotalAmount(totalAmount);
        // 应付总额,订单总额加运费
        orderEntity.setPayAmount(totalAmount.add(orderEntity.getFreightAmount()));
        orderEntity.setCouponAmount(couponAmount);
        orderEntity.setIntegrationAmount(integrationAmount);
        orderEntity.setPromotionAmount(promotionAmount);
        orderEntity.setGrowth(growth);
        orderEntity.setIntegration(integration);
    }

    /**
     * 构建订单信息
     * @param orderSubmitVo
     * @param orderSn
     * @return
     */
    private OrderEntity createOrderEntity(OrderSubmitVo orderSubmitVo, String orderSn) {
        OrderEntity entity = new OrderEntity();

        // 1.订单号信息
        entity.setOrderSn(orderSn);
        MemberRespVo loginUser = LoginUserInterceptor.loginUser.get();
        entity.setMemberId(loginUser.getId());

        // 2.收获地址信息
        R fare = wareFeignService.getFare(orderSubmitVo.getAddrId());
        // 运费信息
        FareRespVo fareVo = fare.getData(new TypeReference<FareRespVo>() {});
        entity.setFreightAmount(fareVo.getFare());
        MemberAddressVo addressVo = fareVo.getMemberAddressVo();
        // 收货人信息
        entity.setReceiverCity(addressVo.getCity());
        entity.setReceiverDetailAddress(addressVo.getDetailAddress());
        entity.setReceiverName(addressVo.getName());
        entity.setReceiverPhone(addressVo.getPhone());
        entity.setReceiverPostCode(addressVo.getPostCode());
        entity.setReceiverProvince(addressVo.getProvince());
        entity.setReceiverRegion(addressVo.getRegion());

        // 3.设置订单状态
        entity.setStatus(OrderStatusEnum.CREATE_NEW.getCode());
        // 订单确认期限,默认7天
        entity.setAutoConfirmDay(7);
        // 0代表未删除
        entity.setDeleteStatus(0);

        return entity;
    }

    /**
     * 构建订单项列表
     * @param orderSn
     * @return
     */
    private List<OrderItemEntity> createOrderItems(String orderSn) {
        List<OrderItemVo> orderItems = cartFeignService.getCurrentUserCartItems();
        if(!CollectionUtils.isEmpty(orderItems)) {
            List<OrderItemEntity> itemEntities = orderItems.stream().map(orderItem -> {
                OrderItemEntity itemEntity = createOrderItem(orderItem);
                itemEntity.setOrderSn(orderSn);
                return itemEntity;
            }).collect(Collectors.toList());
            return itemEntities;
        }
        return null;
    }

    /**
     * 构建订单项信息
     * @param orderItem
     * @return
     */
    private OrderItemEntity createOrderItem(OrderItemVo orderItem) {
        OrderItemEntity itemEntity = new OrderItemEntity();
        // 1.订单信息:订单号

        // 2.商品SPU信息
        Long skuId = orderItem.getSkuId();
        R r = productFeignService.getSpuInfoBySkuId(skuId);
        SpuInfoVo spuInfoVo = r.getData(new TypeReference<SpuInfoVo>() {});
        itemEntity.setSpuId(spuInfoVo.getId());
        itemEntity.setSpuBrand(spuInfoVo.getBrandId().toString());
        itemEntity.setSpuName(spuInfoVo.getSpuName());
        itemEntity.setCategoryId(spuInfoVo.getCatalogId());

        // 3.商品SKU信息
        itemEntity.setSkuId(orderItem.getSkuId());
        itemEntity.setSkuName(orderItem.getTitle());
        itemEntity.setSkuPic(orderItem.getImage());
        itemEntity.setSkuPrice(orderItem.getPrice());
        String skuAttr = StringUtils.collectionToDelimitedString(orderItem.getSkuAttr(), ";");
        itemEntity.setSkuAttrsVals(skuAttr);
        itemEntity.setSkuQuantity(orderItem.getCount());

        // 4.优惠信息 忽略

        // 5.积分信息
        itemEntity.setGiftGrowth(orderItem.getPrice().intValue() * orderItem.getCount());
        itemEntity.setGiftIntegration(orderItem.getPrice().intValue() * orderItem.getCount());

        // 6.订单项价格信息
        itemEntity.setPromotionAmount(new BigDecimal("0.0"));
        itemEntity.setCouponAmount(new BigDecimal("0.0"));
        itemEntity.setIntegrationAmount(new BigDecimal("0.0"));
        BigDecimal originAmount = itemEntity.getSkuPrice().multiply(new BigDecimal(itemEntity.getSkuQuantity().toString()));
        // 实际金额,总额减各种优惠
        BigDecimal realAmount = originAmount.subtract(itemEntity.getPromotionAmount())
                .subtract(itemEntity.getCouponAmount()).subtract(itemEntity.getIntegrationAmount());
        itemEntity.setRealAmount(realAmount);

        return itemEntity;
    }

}